<template>
  <div class="player" v-show="playlist.length > 0">
    <transition name="normal" @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave">
      <div class="normal-player" v-show="fullScreen">
        <div class="background">
          <img width="100%" height="100%" :src="currentSong.image + '?param=1024y1024'" />
        </div>
        <div class="top">
          <div class="back">
            <i class="icon-back" @click="back"></i>
          </div>
          <h1 class="title" v-html="currentSong.name"></h1>
          <h2 class="subtitle" v-html="currentSong.singer"></h2>
        </div>
        <div
          class="middle"
          @touchstart.prevent="middleTouchStart"
          @touchmove.prevent="middleTouchMove"
          @touchend="middleTouchEnd"
        >
          <div class="middle-l" ref="middleL">
            <div class="cd-wrapper" ref="cdWrapper">
              <div class="cd" :class="playing ? 'play' : 'play pause'">
                <img class="image" :src="currentSong.image + '?param=1024y1024'" />
              </div>
            </div>
            <div class="playing-lyric-wrapper">
              <div class="playing-lyric">{{ playingLyric }}</div>
            </div>
          </div>
          <scroll class="middle-r" ref="lyricList" :data="currentLyric && currentLyric.lyric">
            <div class="lyric-wrapper">
              <div v-if="currentLyric">
                <p
                  ref="lyricLine"
                  :class="{ current: currentLineNum === index }"
                  class="text"
                  v-for="(line, index) in currentLyric.lyric"
                  :key="index"
                >
                  {{ line.content }}
                </p>
              </div>
            </div>
          </scroll>
        </div>
        <div class="bottom">
          <div class="dot-wrapper">
            <span class="dot" :class="{ active: currentShow === 'cd' }"></span>
            <span class="dot" :class="{ active: currentShow === 'lyric' }"></span>
          </div>
          <div class="progress-wrapper">
            <span class="time time-l">{{ currentTime | formatTime('mm:ss') }}</span>
            <div class="progress-bar-wrapper">
              <progress-bar ref="progressBar" v-model="percent"></progress-bar>
            </div>
            <span class="time time-r">{{ currentSong.duration | formatTime('mm:ss') }}</span>
          </div>
          <div class="operators">
            <div class="icon i-left" @click="changeMode">
              <i :class="iconMode"></i>
            </div>
            <div class="icon i-left" :class="disableCls">
              <i class="icon-prev" @click="prev(1)"></i>
            </div>
            <div class="icon i-center" :class="disableCls">
              <i @click="togglePlaying" :class="playing ? 'icon-pause' : 'icon-play'"></i>
            </div>
            <div class="icon i-right" :class="disableCls">
              <i class="icon-next" @click="next(1)"></i>
            </div>
            <div class="icon i-right">
              <i :class="getFavoriteIcon(currentSong)" @click="toggleFavorite(currentSong)"></i>
            </div>
          </div>
        </div>
      </div>
    </transition>
    <transition name="mini">
      <div class="mini-player" v-show="!fullScreen" @click="open">
        <div class="icon">
          <img
            :class="playing ? 'play' : 'play pause'"
            width="40"
            height="40"
            :src="currentSong.image + '?param=128y128'"
          />
        </div>
        <div class="text">
          <h2 class="name" v-html="currentSong.name"></h2>
          <p class="desc" v-html="currentSong.singer"></p>
        </div>
        <div class="control">
          <progress-circle :percent="percent" :radius="miniPlayRadius">
            <i
              @click.stop="togglePlaying"
              class="icon-mini"
              :class="playing ? 'icon-pause-mini' : 'icon-play-mini'"
            ></i>
          </progress-circle>
        </div>
        <div class="control " @click.stop="showPlaylist">
          <i class="icon-playlist"></i>
        </div>
      </div>
    </transition>
    <playlist ref="playlist"></playlist>
    <audio
      ref="audio"
      :src="currentSong.url"
      @timeupdate="audioUpdateTime"
      @ended="audioEnd"
      @play="audioReady"
      @error="audioError"
    ></audio>
  </div>
</template>
<script>
import animations from 'create-keyframe-animation';
import { prefixStyle } from 'common/js/dom';
import ProgressBar from 'components/progress-bar/progress-bar';
import Scroll from '@/components/scroll/scroll';
import Playlist from '@/views/playlist/playlist';
import ProgressCircle from 'components/progress-circle/progress-circle';
import { playMode } from 'common/js/config';
import lyric from 'common/js/lyrics';
import { playerMixin } from 'common/js/mixin';
import { mapActions } from 'vuex';

const transform = prefixStyle('transform');
const transitionDuration = prefixStyle('transition-duration');

export default {
  mixins: [playerMixin],
  components: { ProgressBar, ProgressCircle, Scroll, Playlist },
  computed: {
    disableCls() {
      return this.songReady ? '' : 'disable';
    },
    percent: {
      get() {
        return this.currentTime / this.currentSong.duration;
      },
      set(newVal) {
        this.$refs.audio.currentTime = newVal * this.currentSong.duration;
        if (!this.playing) this.togglePlaying();
        return newVal;
      },
    },
  },
  data() {
    return {
      songReady: false,
      currentTime: 0,
      miniPlayRadius: 32,
      currentLyric: null,
      currentLineNum: 0,
      currentShow: 'cd',
      touch: {},
      playingLyric: '',
      lyricTime: null,
    };
  },
  watch: {
    currentSong(newSong, oldSong) {
      if (!newSong.id) return;
      if (newSong.id === oldSong.id) return;
      clearTimeout(this.time);
      this.time = setTimeout(() => {
        this.$refs.audio.play();
        this.getLyric();
      }, 1000);
    },
    playing(flag) {
      this.$nextTick(() => {
        flag ? this.$refs.audio.play() : this.$refs.audio.pause();
      });
    },
    currentTime(newTime) {
      if (!this.currentLyric) return;
      let index = this.currentLyric.seek(newTime);
      if (index < 0) index = 0;
      if (index !== this.currentLineNum) {
        if (this.currentLineNum > 5) {
          let el = this.$refs.lyricLine[this.currentLineNum - 5];
          this.$refs.lyricList.scrollToElement(el, 1000);
        } else {
          this.$refs.lyricList.scrollTo(0, 0, 1000);
        }
      }
      this.currentLineNum = index;
      this.playingLyric = this.currentLyric.lyric[this.currentLineNum].content;
    },
  },
  methods: {
    ...mapActions(['savePlayHistory']),
    back() {
      this.setFullScreen(false);
    },
    open() {
      this.setFullScreen(true);
    },
    showPlaylist() {
      this.$refs.playlist.show();
    },
    togglePlaying() {
      this.setPlayingState(!this.playing);
    },
    prev(size) {
      if (!this.songReady) return;
      let canPlayLength = 0;
      this.playlist.forEach((item) => {
        if (item.canplay) canPlayLength++;
      });
      if (canPlayLength === 1) {
        this.loop();
        return;
      }
      let index = this.currentIndex - size;
      if (index < 0) {
        index = this.playlist.length - size + 1;
      }
      if (this.playlist[index].canplay) {
        this.setCurrentIndex(index);
        if (!this.playing) {
          this.togglePlaying();
        }
      } else {
        this.prev(size + 1);
        return;
      }
      this.songReady = false;
    },
    next(size) {
      if (!this.songReady) return;
      let canPlayLength = 0;
      this.playlist.forEach((item) => {
        if (item.canplay) canPlayLength++;
      });
      if (canPlayLength === 1) {
        this.loop();
        return;
      }
      let index = this.currentIndex + size;
      if (index >= this.playlist.length) {
        index = size - 1;
      }
      if (this.playlist[index].canplay) {
        this.setCurrentIndex(index);
        if (!this.playing) {
          this.togglePlaying();
        }
      } else {
        this.next(size + 1);
        return;
      }
      this.songReady = false;
    },
    loop() {
      this.$refs.audio.currentTime = 0;
      this.playingLyric = '';
      this.currentLineNum = 0;
      this.$refs.audio.play();
      this.setPlayingState(true);
    },
    getLyric() {
      this.currentSong.getLyric().then((res) => {
        this.currentLyric = new lyric(res);
      });
    },
    middleTouchStart(e) {
      // 用来判断是否是一次移动
      this.touch.moved = false;
      this.touch.initiated = true;
      // 记录 x y坐标
      this.touch.startX = e.touches[0].pageX;
      this.touch.startY = e.touches[0].pageY;
    },
    middleTouchMove(e) {
      if (!this.touch.initiated) {
        return;
      }
      // 计算偏移量
      const deltaX = e.touches[0].pageX - this.touch.startX;
      const deltaY = e.touches[0].pageY - this.touch.startY;
      // 纵向偏移什么都不做
      if (Math.abs(deltaY) > Math.abs(deltaX)) return;
      if (!this.touch.moved) {
        this.touch.moved = true;
      }
      const left = this.currentShow === 'cd' ? 0 : -window.innerWidth;
      const offsetWidth = Math.min(0, Math.max(-window.innerWidth, left + deltaX));
      this.touch.percent = Math.abs(offsetWidth / window.innerWidth);
      this.$refs.lyricList.$el.style[transform] = `translate3d(${offsetWidth}px,0,0)`;
      this.$refs.lyricList.$el.style[transitionDuration] = 0;
      this.$refs.middleL.style.opacity = 1 - this.touch.percent;
      this.$refs.middleL.style[transitionDuration] = 0;
    },
    middleTouchEnd() {
      if (!this.touch.moved) {
        return;
      }
      let offsetWidth;
      let opacity;
      // 从左向右划和从右向左划，并且滑动的距离要大于 10%
      if (this.currentShow === 'cd') {
        if (this.touch.percent > 0.1) {
          offsetWidth = -window.innerWidth;
          opacity = 0;
          this.currentShow = 'lyric';
        } else {
          offsetWidth = 0;
          opacity = 1;
        }
      } else {
        if (this.touch.percent < 0.9) {
          offsetWidth = 0;
          this.currentShow = 'cd';
          opacity = 1;
        } else {
          offsetWidth = -window.innerWidth;
          opacity = 0;
        }
      }
      const time = 300;
      this.$refs.lyricList.$el.style[transform] = `translate3d(${offsetWidth}px,0,0)`;
      this.$refs.lyricList.$el.style[transitionDuration] = `${time}ms`;
      this.$refs.middleL.style.opacity = opacity;
      this.$refs.middleL.style[transitionDuration] = `${time}ms`;
      this.touch.initiated = false;
    },
    audioReady() {
      this.songReady = true;
      this.savePlayHistory(this.currentSong);
    },
    audioError() {
      this.songReady = true;
    },
    audioUpdateTime(e) {
      this.currentTime = e.target.currentTime;
    },
    audioEnd() {
      if (this.mode === playMode.loop) {
        this.loop();
      } else {
        this.next(1);
      }
    },
    enter(el, done) {
      // 进入的时候需要 图片 放大缩小一下需要使用 css3的动画
      const { x, y, scale } = this._getPosAndScale();
      // 定义动画
      let animation = {
        0: { transform: `translate3d(${x}px,${y}px,0) scale(${scale})` },
        60: { transform: `translate3d(0,0,0) scale(1.1)` },
        100: { transform: `translate3d(0,0,0) scale(1)` },
      };
      // 注册动画
      animations.registerAnimation({
        name: 'move',
        animation,
        presets: { duration: 400, easing: 'linear' },
      });
      // 执行动画
      animations.runAnimation(this.$refs.cdWrapper, 'move', done);
    },
    afterEnter() {
      animations.unregisterAnimation('move');
      this.$refs.cdWrapper.style.animation = '';
    },
    leave(el, done) {
      // 离开的时候只需要 缩小动画 直接用 css属性控制
      this.$refs.cdWrapper.style.transition = 'all 0.4s';
      const { x, y, scale } = this._getPosAndScale();
      this.$refs.cdWrapper.style[transform] = `translate3d(${x}px,${y}px,0) scale(${scale})`;
      this.$refs.cdWrapper.addEventListener('transitionend', done);
    },
    afterLeave() {
      this.$refs.cdWrapper.style.transition = '';
      this.$refs.cdWrapper.style[transform] = '';
    },
    _getPosAndScale() {
      const targetWidth = 40; // 小播放器宽度
      const paddingLeft = 40; // 小播放器左 padding
      const paddingBottom = 30; // 小播放器下 padding
      const paddingTop = 80; // 大播放器上 padding
      const width = window.innerWidth * 0.8; // 大播放器的宽度
      const scale = targetWidth / width; // 大小播放器 初始的缩放比例
      const x = -(window.innerWidth / 2 - paddingLeft); // 大小播放器中心点 x 的偏移
      const y = window.innerHeight - paddingTop - width / 2 - paddingBottom; // 大小播放器中心点 y 的偏移
      return { x, y, scale };
    },
  },
};
</script>
<style scoped lang="stylus">
@import "~common/stylus/variable"
@import "~common/stylus/mixin"

.player
  .normal-player
    position: fixed
    left: 0
    right: 0
    top: 0
    bottom: 0
    z-index: 150
    background: $color-background
    .background
      position: absolute
      left: 0
      top: 0
      width: 100%
      height: 100%
      z-index: -1
      opacity: 0.6
      filter: blur(20px)
    .top
      position: relative
      margin-bottom: 25px
      .back
        position absolute
        top: 0
        left: 6px
        z-index: 50
        .icon-back
          display: block
          padding: 9px
          font-size: $font-size-large-x
          color: $color-theme
          transform: rotate(-90deg)
      .title
        width: 70%
        margin: 0 auto
        line-height: 40px
        text-align: center
        no-wrap()
        font-size: $font-size-large
        color: $color-text
      .subtitle
        line-height: 20px
        text-align: center
        font-size: $font-size-medium
        color: $color-text
    .middle
      position: fixed
      width: 100%
      top: 80px
      bottom: 170px
      white-space: nowrap
      font-size: 0
      .middle-l
        display: inline-block
        vertical-align: top
        position: relative
        width: 100%
        height: 0
        padding-top: 80%
        .cd-wrapper
          position: absolute
          left: 10%
          top: 0
          width: 80%
          height: 100%
          .cd
            width: 100%
            height: 100%
            box-sizing: border-box
            border: 10px solid rgba(255, 255, 255, 0.1)
            border-radius: 50%
            &.play
              animation: rotate 20s linear infinite
            &.pause
              animation-play-state: paused
            .image
              position: absolute
              left: 0
              top: 0
              width: 100%
              height: 100%
              border-radius: 50%

        .playing-lyric-wrapper
          width: 80%
          margin: 30px auto 0 auto
          overflow: hidden
          text-align: center
          .playing-lyric
            height: 20px
            line-height: 20px
            font-size: $font-size-medium
            color: $color-text-l
      .middle-r
        display: inline-block
        vertical-align: top
        width: 100%
        height: 100%
        overflow: hidden
        .lyric-wrapper
          width: 80%
          margin: 0 auto
          overflow: hidden
          text-align: center
          .text
            line-height: 32px
            color: $color-text-l
            font-size: $font-size-medium
            &.current
              color: $color-text
    .bottom
      position: absolute
      bottom: 50px
      width: 100%
      .dot-wrapper
        text-align: center
        font-size: 0
        .dot
          display: inline-block
          vertical-align: middle
          margin: 0 4px
          width: 8px
          height: 8px
          border-radius: 50%
          background: $color-text-l
          &.active
            width: 20px
            border-radius: 5px
            background: $color-text-ll
      .progress-wrapper
        display: flex
        align-items: center
        width: 80%
        margin: 0px auto
        padding: 10px 0
        .time
          color: $color-text
          font-size: $font-size-small
          flex: 0 0 35px
          line-height: 30px
          width: 35px
          &.time-l
            text-align: left
          &.time-r
            text-align: right
        .progress-bar-wrapper
          flex: 1
      .operators
        display: flex
        align-items: center
        .icon
          flex: 1
          color: $color-theme
          &.disable
            color: $color-theme-d
          i
            font-size: 30px
        .i-left
          text-align: right
        .i-center
          padding: 0 20px
          text-align: center
          i
            font-size: 40px
        .i-right
          text-align: left
        .icon-favorite
          color: $color-sub-theme
    &.normal-enter-active, &.normal-leave-active
      transition: all 0.4s
      .top, .bottom
        transition: all 0.4s cubic-bezier(0.86, 0.18, 0.82, 1.32)
    &.normal-enter, &.normal-leave-to
      opacity: 0
      .top
        transform: translate3d(0, -100px, 0)
      .bottom
        transform: translate3d(0, 100px, 0)
  .mini-player
    display: flex
    align-items: center
    position: fixed
    left: 0
    bottom: 0
    z-index: 180
    width: 100%
    height: 60px
    background: $color-highlight-background
    &.mini-enter-active, &.mini-leave-active
      transition: all 0.4s
    &.mini-enter, &.mini-leave-to
      opacity: 0
    .icon
      flex: 0 0 40px
      width: 40px
      padding: 0 10px 0 20px
      img
        border-radius: 50%
        &.play
          animation: rotate 10s linear infinite
        &.pause
          animation-play-state: paused
    .text
      display: flex
      flex-direction: column
      justify-content: center
      flex: 1
      line-height: 20px
      overflow: hidden
      .name
        margin-bottom: 2px
        no-wrap()
        font-size: $font-size-medium
        color: $color-text
      .desc
        no-wrap()
        font-size: $font-size-small
        color: $color-text-d
    .control
      flex: 0 0 30px
      width: 30px
      padding: 0 10px
      .icon-play-mini, .icon-pause-mini, .icon-playlist
        font-size: 30px
        color: $color-theme-d
      .icon-mini
        font-size: 32px
        position: absolute
        left: 0
        top: 0

@keyframes rotate
  0%
    transform: rotate(0)
  100%
    transform: rotate(360deg)
</style>
